/**
 * 
 */
package es.esi.gemde.modeltransformator.acceleoengine;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import org.eclipse.acceleo.model.mtl.resource.AcceleoResourceSetImpl;
import org.eclipse.acceleo.parser.AcceleoSourceBuffer;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.BasicMonitor;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;

import es.esi.gemde.core.utils.GEMDEUtils;
import es.esi.gemde.modeltransformator.exceptions.TransformationEngineException;
import es.esi.gemde.modeltransformator.service.ITransformation;
import es.esi.gemde.modeltransformator.service.ITransformationEngine;

/**
 * Implementation of the GEMDE connector for ACCELEO.
 * 
 * @author adrian.noguero@tecnalia.com
 */
public class AcceleoEngine implements ITransformationEngine {

	private static final String ENGINE_NAME = "Acceleo";
	private AcceleoResourceSetImpl rs = null;
	private String logs = null;
	
	/**
	 * Public constructor of the engine
	 */
	public AcceleoEngine() {
		
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modeltransformator.service.ITransformationEngine#getName()
	 */
	@Override
	public String getName() {
		return ENGINE_NAME;
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modeltransformator.service.ITransformationEngine#checkTransformationURIValidity(java.util.List)
	 */
	@Override
	public boolean checkTransformationURIValidity(List<URI> uris) {
		if (uris.size() == 0) {
			return false;
		}
		
		rs = new AcceleoResourceSetImpl();
		
		Vector<AcceleoSourceBuffer> inputs = new Vector<AcceleoSourceBuffer>();
		
		// Create the ASTs of each of the input files
		for (URI uri : uris) {
			File input = GEMDEUtils.uri2file(uri);
			AcceleoSourceBuffer sb = new AcceleoSourceBuffer(input);
			Resource res = rs.createResource(org.eclipse.emf.common.util.URI.createURI(uri.toString()));
			
			sb.createCST();
			sb.createAST(res);
			
			inputs.add(sb);
		}
		
		// Resolve the dependencies of the ASTs
		for (AcceleoSourceBuffer sb : inputs) {
			sb.resolveAST();
		}
		
		// Check for errors
		for (AcceleoSourceBuffer sb : inputs) {
			if (!sb.getProblems().getList().isEmpty()) {
				AcceleoEngineActivator.cleanupDir();
				return false;
			}
		}
		
		// Find a MAIN transformation
		int mainCount = 0;
		for (AcceleoSourceBuffer sb : inputs) {
			if (AcceleoEngineActivator.getMainTemplate(sb) != null) {
				mainCount++;
			}
		}
		
		AcceleoEngineActivator.cleanupDir();
		return mainCount == 1;
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modeltransformator.service.ITransformationEngine#executeTransformation(org.eclipse.emf.ecore.EObject[], es.esi.gemde.modeltransformator.service.ITransformation, java.lang.String)
	 */
	@Override
	public IStatus executeTransformation(EObject[] inputs,
			ITransformation transformation, String outputPath)
			throws IllegalArgumentException, TransformationEngineException {
		
		// Initial checks
		if (inputs == null){
			throw new IllegalArgumentException("A null inputs array was provided");
		}
		
		if (transformation == null){
			throw new IllegalArgumentException("A null transformation was provided");
		}
		
		if (outputPath == null){
			throw new IllegalArgumentException("A null outputPath was provided");
		}
		
		if (transformation.getRequiredInputList().size() != inputs.length) {
			throw new IllegalArgumentException("The number of provided inputs doesn't match the number of required inputs for this transformation");
		}
		
		for (int i = 0; i < inputs.length; i++) {
			EClass c = transformation.getRequiredInputList().get(i);
			if (!(c.isInstance(inputs[i]))) {
				throw new IllegalArgumentException("Provided input \"" + inputs[i].eClass().getInstanceTypeName() + "\" doesn't match the required type \"" + c.getName() + "\"");
			}
		}
		
//		rs = new AcceleoResourceSetImpl();
//		
//		// For handling messages
//		logs = new String();
//		
//		Vector<AcceleoSourceBuffer> buffers = new Vector<AcceleoSourceBuffer>();
//
//		// Create the ASTs of each of the input files
//		for (URI uri : transformation.getTransformationURIs()) {
//			File input = GEMDEUtils.uri2file(uri);
//			AcceleoSourceBuffer sb = new AcceleoSourceBuffer(input);
//			Resource res = rs.createResource(org.eclipse.emf.common.util.URI.createURI(uri.toString()));
//
//			sb.createCST();
//			sb.createAST(res);
//
//			buffers.add(sb);
//		}
//
//		// Resolve the dependencies of the ASTs
//		for (AcceleoSourceBuffer sb : buffers) {
//			sb.resolveAST();
//		}
//
//		// Check for errors
//		String errors = "";
//		for (AcceleoSourceBuffer sb : buffers) {
//			for (AcceleoParserProblem p : sb.getProblems().getList()) {
//				errors += p.getMessage() + "\n";
//			}
//		}
//		
//		if (!errors.equals("")) {
//			return new Status(IStatus.ERROR, AcceleoEngineActivator.PLUGIN_ID, errors);  
//		}
//		
//		// Find a MAIN transformation
//		int mainCount = 0;
//		Template tpl = null;
//		for (AcceleoSourceBuffer sb : buffers) {
//			Template temp = AcceleoEngineActivator.getMainTemplate(sb);
//			if (temp != null) {
//				mainCount++;
//				tpl = temp;
//			}
//		}
//		
//		if (mainCount != 1) {
//			return new Status(IStatus.ERROR, AcceleoEngineActivator.PLUGIN_ID, "Couldn't find a valid @main template.");  
//		}

		// Invoke the Acceleo Service to generate the template
//		AcceleoService s = new AcceleoService();
//		s.addListener(new IAcceleoTextGenerationListener() {
//			
//			@Override
//			public void textGenerated(AcceleoTextGenerationEvent event) {
//				logs += event.getText() + "\n";
//			}
//			
//			@Override
//			public boolean listensToGenerationEnd() {org.eclipse.acceleo.common.internal.utils.AcceleoPackageRegistry
//				return true;
//			}
//			
//			@Override
//			public void generationEnd(AcceleoTextGenerationEvent event) {
//				logs += "Generation Ended!!\n";
//			}
//			
//			@Override
//			public void filePathComputed(AcceleoTextGenerationEvent event) {}
//			
//			@Override
//			public void fileGenerated(AcceleoTextGenerationEvent event) {}
//		});
//		
//		// Generate!
//		Vector<EObject> inputList = new Vector<EObject>();
//		for (EObject o : inputs) {
//			inputList.add(o);
//		}
//		
//		s.doGenerateTemplate(tpl, inputList, new File(outputPath), true, null);
		
		
		GEMDEAcceleoGenerator generator = new GEMDEAcceleoGenerator(inputs, transformation, outputPath);
		generator.initialize();
		
		if (!generator.isInitialized()) {
			return new Status(IStatus.ERROR, AcceleoEngineActivator.PLUGIN_ID, generator.getErrors());
		}
		
		try {
			generator.generate(new BasicMonitor());
		} catch (IOException e) {
			throw new TransformationEngineException(e);
		}

		// Everything went OK!!
		AcceleoEngineActivator.cleanupDir();
	    return new Status(IStatus.OK, AcceleoEngineActivator.PLUGIN_ID, logs);
	}

	@Override
	public String[] getAdditionalOptionsList() {
		return new String[0];
	}

	@Override
	public boolean checkTransformationParamsValidity(
			HashMap<String, EClass> inputs, HashMap<String, EClass> outputs,
			HashMap<String, String> options) {
		return inputs.size() > 0;
	}

}
